/*
 * Copyright (C) 2016+     AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-GPL2
 * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
 * Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
 */

/* ScriptData
SDName: Winterspring
SD%Complete: Almost Completely Emptied
SDComment: Vendor Rivern Frostwind. Quest Support 4901
SDCategory: Winterspring
EndScriptData */

/* ContentData
npc_rivern_frostwind
npc_ranshalla
go_elune_fire
EndContentData */

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "ScriptedEscortAI.h"
#include "Player.h"
#include "WorldSession.h"

// Ours
enum eStaveQuest
{
    QUEST_STAVE_OF_THE_ANCIENTS             = 7636,

    NPC_SIMONE_NORMAL                       = 14527,
    NPC_FRANKLIN_NORMAL                     = 14529,
    NPC_ARTORIUS_NORMAL                     = 14531,
    NPC_NELSON_NORMAL                       = 14536,
    NPC_PRECIOUS                            = 14528,

    NPC_SIMONE_EVIL                         = 14533,
    NPC_FRANKLIN_EVIL                       = 14534,
    NPC_ARTORIUS_EVIL                       = 14535,
    NPC_NELSON_EVIL                         = 14530,
    NPC_PRECIOUS_EVIL                       = 14538,

    EVENT_CHECK_PLAYER                      = 1,
    EVENT_SPELL_CHAIN_LIGHTNING             = 23206,
    EVENT_SPELL_TEMPTRESS_KISS              = 23205,
    EVENT_SPELL_DEMONIC_ENRAGE              = 23257,
    EVENT_SPELL_ENTROPIC_STING              = 23260,
    EVENT_SPELL_DEMONIC_DOOM                = 23298,
    EVENT_SPELL_STINGING_TRAUMA             = 23299,
    EVENT_SPELL_DREADFUL_FRIGHT             = 23275,

    SPELL_FOOLS_PLIGHT                      = 23504,
    SPELL_SOUL_FLAME                        = 23272,
};

class npc_stave_of_the_ancients : public CreatureScript
{
public:
    npc_stave_of_the_ancients() : CreatureScript("npc_stave_of_the_ancients") { }

    CreatureAI* GetAI(Creature* creature) const
    {
        return new npc_stave_of_the_ancientsAI(creature);
    }

    struct npc_stave_of_the_ancientsAI : public ScriptedAI
    {
        npc_stave_of_the_ancientsAI(Creature* creature) : ScriptedAI(creature)
        {
            changeEntry = me->GetEntry();
            switch (me->GetEntry())
            {
                case NPC_SIMONE_NORMAL:
                    changeEntry = NPC_SIMONE_EVIL;
                    break;
                case NPC_FRANKLIN_NORMAL:
                    changeEntry = NPC_FRANKLIN_EVIL;
                    break;
                case NPC_ARTORIUS_NORMAL:
                    changeEntry = NPC_ARTORIUS_EVIL;
                    break;
                case NPC_NELSON_NORMAL:
                    changeEntry = NPC_NELSON_EVIL;
                    break;
                case NPC_PRECIOUS:
                    changeEntry = NPC_PRECIOUS_EVIL;
                    break;
            }
        }

        uint64 playerGUID;
        EventMap events;
        uint32 changeEntry;
        bool damaged;

        void Reset()
        {
            if (me->GetOriginalEntry() != me->GetEntry())
                me->UpdateEntry(me->GetOriginalEntry());

            events.Reset();
            playerGUID = 0;
            damaged = false;
        }

        void DamageTaken(Unit* who, uint32&, DamageEffectType, SpellSchoolMask)
        {
            if (!damaged)
            {
                if (who && who->GetGUID() != playerGUID && (who->GetTypeId() == TYPEID_PLAYER || IS_PLAYER_GUID(who->GetOwnerGUID())))
                {
                    damaged = true;
                    me->CastSpell(who, SPELL_FOOLS_PLIGHT, true);
                }
            }
            else
                damaged = false;
        }

        void EnterCombat(Unit*)
        {
            switch (changeEntry)
            {
                case NPC_SIMONE_EVIL:
                    events.ScheduleEvent(EVENT_SPELL_CHAIN_LIGHTNING, 3000);
                    events.ScheduleEvent(EVENT_SPELL_TEMPTRESS_KISS, 1000);
                    break;
                case NPC_FRANKLIN_EVIL:
                    events.ScheduleEvent(EVENT_SPELL_DEMONIC_ENRAGE, 3000);
                    events.ScheduleEvent(EVENT_SPELL_ENTROPIC_STING, 5000);
                    break;
                case NPC_ARTORIUS_EVIL:
                    events.ScheduleEvent(EVENT_SPELL_DEMONIC_DOOM, 3000);
                    events.ScheduleEvent(EVENT_SPELL_STINGING_TRAUMA, 5000);
                    break;
                case NPC_NELSON_EVIL:
                    me->CastSpell(me, SPELL_SOUL_FLAME, true);
                    events.ScheduleEvent(EVENT_SPELL_DREADFUL_FRIGHT, 5000);
                    break;
            }
        }

        void MoveInLineOfSight(Unit* who)
        {
            if (me->GetEntry() != changeEntry && who->GetTypeId() == TYPEID_PLAYER && who->ToPlayer()->GetQuestStatus(QUEST_STAVE_OF_THE_ANCIENTS) == QUEST_STATUS_INCOMPLETE)
            {
                playerGUID = who->GetGUID();
                me->UpdateEntry(changeEntry);
                events.ScheduleEvent(EVENT_CHECK_PLAYER, 5000);
                return;
            }
            ScriptedAI::MoveInLineOfSight(who);
        }

        void UpdateAI(uint32 diff)
        {
            events.Update(diff);
            uint32 eventId = events.ExecuteEvent();
            if (eventId == EVENT_CHECK_PLAYER)
            {
                Player* player = ObjectAccessor::GetPlayer(*me, playerGUID);
                if (!player || !player->IsWithinDist2d(me, 60.0f))
                    EnterEvadeMode();
                else
                    events.RepeatEvent(5000);
                return;
            }

            if (!UpdateVictim())
                return;

            if (me->HasUnitState(UNIT_STATE_CASTING))
                return;

            switch (eventId)
            {
                case EVENT_SPELL_CHAIN_LIGHTNING:
                    me->CastSpell(me->GetVictim(), eventId, false);
                    events.RepeatEvent(7000);
                    break;
                case EVENT_SPELL_TEMPTRESS_KISS:
                    me->CastSpell(me->GetVictim(), eventId, false);
                    events.RepeatEvent(45000);
                    break;
                case EVENT_SPELL_DEMONIC_ENRAGE:
                    me->CastSpell(me, eventId, false);
                    events.RepeatEvent(20000);
                    break;
                case EVENT_SPELL_ENTROPIC_STING:
                    me->CastSpell(me->GetVictim(), eventId, false);
                    events.RepeatEvent(20000);
                    break;
                case EVENT_SPELL_DEMONIC_DOOM:
                    me->CastSpell(me->GetVictim(), eventId, false);
                    events.RepeatEvent(50000);
                    break;
                case EVENT_SPELL_STINGING_TRAUMA:
                    me->CastSpell(me->GetVictim(), eventId, false);
                    events.RepeatEvent(20000);
                    break;
                case EVENT_SPELL_DREADFUL_FRIGHT:
                    me->CastSpell(me->GetVictim(), eventId, false);
                    events.RepeatEvent(15000);
                    break;
            }

            DoMeleeAttackIfReady();
        }
    };
};


// Theirs
/*######
## npc_rivern_frostwind
######*/

class npc_rivern_frostwind : public CreatureScript
{
public:
    npc_rivern_frostwind() : CreatureScript("npc_rivern_frostwind") { }

    bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
    {
        ClearGossipMenuFor(player);
        if (action == GOSSIP_ACTION_TRADE)
            player->GetSession()->SendListInventory(creature->GetGUID());

        return true;
    }

    bool OnGossipHello(Player* player, Creature* creature) override
    {
        if (creature->IsQuestGiver())
            player->PrepareQuestMenu(creature->GetGUID());

        if (creature->IsVendor() && player->GetReputationRank(589) == REP_EXALTED)
            AddGossipItemFor(player, GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);

        SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());

        return true;
    }
};

enum Says
{
    // Escort texts
    SAY_QUEST_START         = 0,
    SAY_ENTER_OWL_THICKET   = 1,
    SAY_REACH_TORCH         = 2,
    SAY_AFTER_TORCH         = 3,
    SAY_REACH_ALTAR_1       = 4,
    SAY_REACH_ALTAR_2       = 5,

    // After lighting the altar cinematic
    SAY_RANSHALLA_ALTAR_1   = 6,
    SAY_RANSHALLA_ALTAR_2   = 7,
    SAY_PRIESTESS_ALTAR_3   = 8,
    SAY_PRIESTESS_ALTAR_4   = 9,
    SAY_RANSHALLA_ALTAR_5   = 10,
    SAY_RANSHALLA_ALTAR_6   = 11,
    SAY_PRIESTESS_ALTAR_7   = 12,
    SAY_PRIESTESS_ALTAR_8   = 13,
    SAY_PRIESTESS_ALTAR_9   = 14,
    SAY_PRIESTESS_ALTAR_10  = 15,
    SAY_PRIESTESS_ALTAR_11  = 16,
    SAY_PRIESTESS_ALTAR_12  = 17,
    SAY_PRIESTESS_ALTAR_13  = 18,
    SAY_PRIESTESS_ALTAR_14  = 19,
    SAY_VOICE_ALTAR_15      = 20,
    SAY_PRIESTESS_ALTAR_16  = 21,
    SAY_PRIESTESS_ALTAR_17  = 22,
    SAY_PRIESTESS_ALTAR_18  = 23,
    SAY_PRIESTESS_ALTAR_19  = 24,
    SAY_PRIESTESS_ALTAR_20  = 25,
    SAY_PRIESTESS_ALTAR_21  = 26,
    SAY_RANSHALLA_END_1     = 27,
    SAY_RANSHALLA_END_2     = 28,

    EMOTE_CHANT_SPELL       = 29,
};

enum Spells
{
    SPELL_LIGHT_TORCH       = 18953,  // channeled spell by Ranshalla while waiting for the torches / altar
};

enum NPCs
{
    NPC_RANSHALLA           = 10300,
    NPC_PRIESTESS_ELUNE     = 12116,
    NPC_VOICE_ELUNE         = 12152,
    NPC_GUARDIAN_ELUNE      = 12140,
};

enum GOs
{
    GO_ELUNE_ALTAR          = 177404,
    GO_ELUNE_FIRE           = 177417,
    GO_ELUNE_GEM            = 177414, // is respawned in script
    GO_ELUNE_LIGHT          = 177415, // are respawned in script
};

enum Quests
{
    QUEST_GUARDIANS_ALTAR   = 4901,
};

enum Dummies
{
    NPC_PRIESTESS_DATA_1    = -1, // dummy member for the first priestess (right)
    NPC_PRIESTESS_DATA_2    = -2, // dummy member for the second priestess (left)
    DATA_MOVE_PRIESTESS     = -3, // dummy member to check the priestess movement
    DATA_EVENT_END          = -4, // dummy member to indicate the event end

    EVENT_RESUME            = 1,  // trigger rest of event
};

// DialogueHelper (imported from SD)

struct DialogueEntry
{
    int32 TextEntry;    ///< To be said text entry
    int32 SayerEntry;   ///< Entry of the mob who should say
    uint32 SayTimer;    ///< Time delay until next text of array is said (0 stops)
};

class DialogueHelper
{
public:
    // The array MUST be terminated by {0, 0, 0}
    DialogueHelper(DialogueEntry const* dialogueArray) :
        _dialogueArray(dialogueArray),
        _currentEntry(nullptr),
        _actionTimer(0)
    { }
    // The array MUST be terminated by {0, 0, 0, 0, 0}

    /// Function to initialize the dialogue helper for instances. If not used with instances, GetSpeakerByEntry MUST be overwritten to obtain the speakers
    /// Set if take first entries or second entries

    void StartNextDialogueText(int32 textEntry)
    {
        // Find textEntry
        bool found = false;

        for (DialogueEntry const* entry = _dialogueArray; entry->TextEntry; ++entry)
        {
            if (entry->TextEntry == textEntry)
            {
                _currentEntry = entry;
                found = true;
                break;
            }
        }

        if (!found)
            return;

        DoNextDialogueStep();
    }

    void DialogueUpdate(uint32 diff)
    {
        if (_actionTimer)
        {
            if (_actionTimer <= diff)
                DoNextDialogueStep();
            else
                _actionTimer -= diff;
        }
    }

protected:
    /// Will be called when a dialogue step was done
    virtual void JustDidDialogueStep(int32 /*entry*/) { }
    /// Will be called to get a speaker, MUST be implemented if not used in instances
    virtual Creature* GetSpeakerByEntry(int32 /*entry*/) { return nullptr; }

private:
    void DoNextDialogueStep()
    {
        // Last Dialogue Entry done?
        if (!_currentEntry || !_currentEntry->TextEntry)
        {
            _actionTimer = 0;
            return;
        }

        // Get Text, SpeakerEntry and Timer
        int32 textEntry = _currentEntry->TextEntry;
        uint32 sayerEntry = _currentEntry->SayerEntry;
        _actionTimer = _currentEntry->SayTimer;

        // Simulate Case
        if (sayerEntry && textEntry >= 0)
        {
            // Use Speaker if directly provided
            if (Creature* speaker = GetSpeakerByEntry(sayerEntry))
                speaker->AI()->Talk(textEntry);
        }

        JustDidDialogueStep(_currentEntry->TextEntry);

        // Increment position
        ++_currentEntry;
    }

    DialogueEntry const* _dialogueArray;
    DialogueEntry const* _currentEntry;

    uint32 _actionTimer;
};

const DialogueEntry introDialogue[] =
{
    {SAY_REACH_ALTAR_1,         NPC_RANSHALLA,          2000},
    {SAY_REACH_ALTAR_2,         NPC_RANSHALLA,          3000},
    {NPC_RANSHALLA,             0,                         0}, // start the altar channeling
    {SAY_PRIESTESS_ALTAR_3,     NPC_PRIESTESS_DATA_2,   1000},
    {SAY_PRIESTESS_ALTAR_4,     NPC_PRIESTESS_DATA_1,   4000},
    {SAY_RANSHALLA_ALTAR_5,     NPC_RANSHALLA,          4000},
    {SAY_RANSHALLA_ALTAR_6,     NPC_RANSHALLA,          4000}, // start the escort here
    {SAY_PRIESTESS_ALTAR_7,     NPC_PRIESTESS_DATA_2,   4000},
    {SAY_PRIESTESS_ALTAR_8,     NPC_PRIESTESS_DATA_2,   5000}, // show the gem
    {GO_ELUNE_GEM,              0,                      5000},
    {SAY_PRIESTESS_ALTAR_9,     NPC_PRIESTESS_DATA_1,   4000}, // move priestess 1 near me
    {NPC_PRIESTESS_DATA_1,      0,                      3000},
    {SAY_PRIESTESS_ALTAR_10,    NPC_PRIESTESS_DATA_1,   5000},
    {SAY_PRIESTESS_ALTAR_11,    NPC_PRIESTESS_DATA_1,   4000},
    {SAY_PRIESTESS_ALTAR_12,    NPC_PRIESTESS_DATA_1,   5000},
    {SAY_PRIESTESS_ALTAR_13,    NPC_PRIESTESS_DATA_1,   8000}, // summon voice and guard of elune
    {NPC_VOICE_ELUNE,           0,                     12000},
    {SAY_VOICE_ALTAR_15,        NPC_VOICE_ELUNE,        5000}, // move priestess 2 near me
    {NPC_PRIESTESS_DATA_2,      0,                      3000},
    {SAY_PRIESTESS_ALTAR_16,    NPC_PRIESTESS_DATA_2,   4000},
    {SAY_PRIESTESS_ALTAR_17,    NPC_PRIESTESS_DATA_2,   6000},
    {SAY_PRIESTESS_ALTAR_18,    NPC_PRIESTESS_DATA_1,   5000},
    {SAY_PRIESTESS_ALTAR_19,    NPC_PRIESTESS_DATA_1,   3000}, // move the owlbeast
    {NPC_GUARDIAN_ELUNE,        0,                      2000},
    {SAY_PRIESTESS_ALTAR_20,    NPC_PRIESTESS_DATA_1,   4000}, // move the first priestess up
    {SAY_PRIESTESS_ALTAR_21,    NPC_PRIESTESS_DATA_2,  10000}, // move second priestess up
    {DATA_MOVE_PRIESTESS,       0,                      6000}, // despawn the gem
    {DATA_EVENT_END,            0,                      2000}, // turn towards the player
    {SAY_RANSHALLA_END_2,       NPC_RANSHALLA, 0},
    {0,                         0,                         0},
};

static Position wingThicketLocations[] =
{
    {5515.98f, -4903.43f, 846.30f, 4.58f},  // 0 right priestess summon loc
    {5501.94f, -4920.20f, 848.69f, 6.15f},  // 1 left priestess summon loc
    {5497.35f, -4906.49f, 850.83f, 2.76f},  // 2 guard of elune summon loc
    {5518.38f, -4913.47f, 845.57f, 0.00f},  // 3 right priestess move loc
    {5510.36f, -4921.17f, 846.33f, 0.00f},  // 4 left priestess move loc
    {5511.31f, -4913.82f, 847.17f, 0.00f},  // 5 guard of elune move loc
    {5518.51f, -4917.56f, 845.23f, 0.00f},  // 6 right priestess second move loc
    {5514.40f, -4921.16f, 845.49f, 0.00f}   // 7 left priestess second move loc
};

/*#####
# npc_ranshalla
#####*/

class npc_ranshalla : public CreatureScript
{
public:
    npc_ranshalla() : CreatureScript("npc_ranshalla") { }
    bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest)
    {
        if (quest->GetQuestId() == QUEST_GUARDIANS_ALTAR)
        {
            creature->AI()->Talk(SAY_QUEST_START);
            creature->setFaction(FACTION_ESCORT_A_NEUTRAL_PASSIVE);

            if (npc_ranshallaAI* escortAI = dynamic_cast<npc_ranshallaAI*>(creature->AI()))
                escortAI->Start(false, false, player->GetGUID(), quest);

            return true;
        }

        return false;
    }
    CreatureAI* GetAI(Creature* creature) const
    {
        return new npc_ranshallaAI(creature);
    }

    struct npc_ranshallaAI : public npc_escortAI, private DialogueHelper
    {
        npc_ranshallaAI(Creature* creature) : npc_escortAI(creature),
            DialogueHelper(introDialogue)
        {
            Reset();
        }

        uint32 _delayTimer;

        uint64 _firstPriestessGUID;
        uint64 _secondPriestessGUID;
        uint64 _guardEluneGUID;
        uint64 _voiceEluneGUID;
        uint64 _altarGUID;

        void Reset()
        {
            _delayTimer = 0;
        }

        // Called when the player activates the torch / altar
        void DoContinueEscort(bool isAltarWaypoint = false)
        {
            me->InterruptNonMeleeSpells(false);

            if (isAltarWaypoint)
                Talk(SAY_RANSHALLA_ALTAR_1);
            else
                Talk(SAY_AFTER_TORCH);

            _delayTimer = 2000;
        }

        // Called when Ranshalla starts to channel on a torch / altar
        void DoChannelTorchSpell(bool isAltarWaypoint = false)
        {
            // Check if we are using the fire or the altar and remove the no_interact flag
            if (isAltarWaypoint)
            {
                if (GameObject* go = GetClosestGameObjectWithEntry(me, GO_ELUNE_ALTAR, 10.0f))
                {
                    go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);
                    me->SetFacingToObject(go);
                    _altarGUID = go->GetGUID();
                }
            }
            else if (GameObject* go = GetClosestGameObjectWithEntry(me, GO_ELUNE_FIRE, 10.0f))
                go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);

            // Yell and set escort to pause
            Talk(SAY_REACH_TORCH);
            Talk(EMOTE_CHANT_SPELL);
            SetEscortPaused(true);
            DoCast(me, SPELL_LIGHT_TORCH);
        }

        void DoSummonPriestess()
        {
            // Summon 2 Elune priestess and make each of them move to a different spot
            if (Creature* priestess = me->SummonCreature(NPC_PRIESTESS_ELUNE, wingThicketLocations[0].m_positionX, wingThicketLocations[0].m_positionY, wingThicketLocations[0].m_positionZ, wingThicketLocations[0].m_orientation, TEMPSUMMON_CORPSE_DESPAWN, 0))
            {
                priestess->GetMotionMaster()->MovePoint(0, wingThicketLocations[3].m_positionX, wingThicketLocations[3].m_positionY, wingThicketLocations[3].m_positionZ);
                _firstPriestessGUID = priestess->GetGUID();
            }
            if (Creature* priestess = me->SummonCreature(NPC_PRIESTESS_ELUNE, wingThicketLocations[1].m_positionX, wingThicketLocations[1].m_positionY, wingThicketLocations[1].m_positionZ, wingThicketLocations[1].m_orientation, TEMPSUMMON_CORPSE_DESPAWN, 0))
            {
                // Left priestess should have a distinct move point because she is the one who starts the dialogue at point reach
                priestess->GetMotionMaster()->MovePoint(1, wingThicketLocations[4].m_positionX, wingThicketLocations[4].m_positionY, wingThicketLocations[4].m_positionZ);
                _secondPriestessGUID = priestess->GetGUID();
            }
        }

        void SummonedMovementInform(Creature* summoned, uint32 type, uint32 pointId)
        {
            if (type != POINT_MOTION_TYPE || summoned->GetEntry() != NPC_PRIESTESS_ELUNE || pointId != 1)
                return;

            // Start the dialogue when the priestess reach the altar (they should both reach the point in the same time)
            StartNextDialogueText(SAY_PRIESTESS_ALTAR_3);
        }

        void WaypointReached(uint32 pointId)
        {
            switch (pointId)
            {
                case 3:
                    Talk(SAY_ENTER_OWL_THICKET);
                    break;
                case 10: // Cavern 1
                case 15: // Cavern 2
                case 20: // Cavern 3
                case 25: // Cavern 4
                case 36: // Cavern 5
                    DoChannelTorchSpell();
                    break;
                case 39:
                    StartNextDialogueText(SAY_REACH_ALTAR_1);
                    SetEscortPaused(true);
                    break;
                case 41:
                    {
                        // Search for all nearest lights and respawn them
                        std::list<GameObject*> eluneLights;
                        GetGameObjectListWithEntryInGrid(eluneLights, me, GO_ELUNE_LIGHT, 20.0f);
                        for (std::list<GameObject*>::const_iterator itr = eluneLights.begin(); itr != eluneLights.end(); ++itr)
                        {
                            if ((*itr)->isSpawned())
                                continue;

                            (*itr)->SetRespawnTime(115);
                            (*itr)->Refresh();
                        }

                        if (GameObject* altar = me->GetMap()->GetGameObject(_altarGUID))
                            me->SetFacingToObject(altar);
                        break;
                    }
                case 42:
                    // Summon the 2 priestess
                    SetEscortPaused(true);
                    DoSummonPriestess();
                    Talk(SAY_RANSHALLA_ALTAR_2);
                    events.ScheduleEvent(EVENT_RESUME, 2000);
                    break;
                case 44:
                    // Stop the escort and turn towards the altar
                    SetEscortPaused(true);
                    if (GameObject* altar = me->GetMap()->GetGameObject(_altarGUID))
                        me->SetFacingToObject(altar);
                    break;
            }
        }

        void JustDidDialogueStep(int32 entry)
        {
            switch (entry)
            {
                case NPC_RANSHALLA:
                    // Start the altar channeling
                    DoChannelTorchSpell(true);
                    break;
                case SAY_RANSHALLA_ALTAR_6:
                    SetEscortPaused(false);
                    break;
                case SAY_PRIESTESS_ALTAR_8:
                    // make the gem respawn
                    if (GameObject* gem = GetClosestGameObjectWithEntry(me, GO_ELUNE_GEM, 10.0f))
                    {
                        if (gem->isSpawned())
                            break;

                        gem->SetRespawnTime(90);
                        gem->Refresh();
                    }
                    break;
                case SAY_PRIESTESS_ALTAR_9:
                    // move near the escort npc
                    if (Creature* priestess = me->GetMap()->GetCreature(_firstPriestessGUID))
                        priestess->GetMotionMaster()->MovePoint(0, wingThicketLocations[6].m_positionX, wingThicketLocations[6].m_positionY, wingThicketLocations[6].m_positionZ);
                    break;
                case SAY_PRIESTESS_ALTAR_13:
                    // summon the Guardian of Elune
                    if (Creature* guard = me->SummonCreature(NPC_GUARDIAN_ELUNE, wingThicketLocations[2].m_positionX, wingThicketLocations[2].m_positionY, wingThicketLocations[2].m_positionZ, wingThicketLocations[2].m_orientation, TEMPSUMMON_CORPSE_DESPAWN, 0))
                    {
                        guard->GetMotionMaster()->MovePoint(0, wingThicketLocations[5].m_positionX, wingThicketLocations[5].m_positionY, wingThicketLocations[5].m_positionZ);
                        _guardEluneGUID = guard->GetGUID();
                    }
                    // summon the Voice of Elune
                    if (GameObject* altar = me->GetMap()->GetGameObject(_altarGUID))
                    {
                        if (Creature* voice = me->SummonCreature(NPC_VOICE_ELUNE, altar->GetPositionX(), altar->GetPositionY(), altar->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN, 30000))
                            _voiceEluneGUID = voice->GetGUID();
                    }
                    break;
                case SAY_VOICE_ALTAR_15:
                    // move near the escort npc and continue dialogue
                    if (Creature* priestess = me->GetMap()->GetCreature(_secondPriestessGUID))
                    {
                        priestess->AI()->Talk(SAY_PRIESTESS_ALTAR_14);
                        priestess->GetMotionMaster()->MovePoint(0, wingThicketLocations[7].m_positionX, wingThicketLocations[7].m_positionY, wingThicketLocations[7].m_positionZ);
                    }
                    break;
                case SAY_PRIESTESS_ALTAR_19:
                    // make the voice of elune leave
                    if (Creature* guard = me->GetMap()->GetCreature(_guardEluneGUID))
                    {
                        guard->GetMotionMaster()->MovePoint(0, wingThicketLocations[2].m_positionX, wingThicketLocations[2].m_positionY, wingThicketLocations[2].m_positionZ);
                        guard->DespawnOrUnsummon(4000);
                    }
                    break;
                case SAY_PRIESTESS_ALTAR_20:
                    // make the first priestess leave
                    if (Creature* priestess = me->GetMap()->GetCreature(_firstPriestessGUID))
                    {
                        priestess->GetMotionMaster()->MovePoint(0, wingThicketLocations[0].m_positionX, wingThicketLocations[0].m_positionY, wingThicketLocations[0].m_positionZ);
                        priestess->DespawnOrUnsummon(4000);
                    }
                    break;
                case SAY_PRIESTESS_ALTAR_21:
                    // make the second priestess leave
                    if (Creature* priestess = me->GetMap()->GetCreature(_secondPriestessGUID))
                    {
                        priestess->GetMotionMaster()->MovePoint(0, wingThicketLocations[1].m_positionX, wingThicketLocations[1].m_positionY, wingThicketLocations[1].m_positionZ);
                        priestess->DespawnOrUnsummon(4000);
                    }
                    break;
                case DATA_EVENT_END:
                    // Turn towards the player
                    if (Player* player = GetPlayerForEscort())
                    {
                        me->SetFacingToObject(player);
                        Talk(SAY_RANSHALLA_END_1, player);
                    }
                    break;
                case SAY_RANSHALLA_END_2:
                    // Turn towards the altar and kneel - quest complete
                    if (GameObject* altar = me->GetMap()->GetGameObject(_altarGUID))
                    {
                        me->SetFacingToObject(altar);
                        altar->ResetDoorOrButton();
                    }
                    me->SetStandState(UNIT_STAND_STATE_KNEEL);
                    if (Player* player = GetPlayerForEscort())
                    {
                        player->GroupEventHappens(QUEST_GUARDIANS_ALTAR, me);
                        Talk(SAY_RANSHALLA_END_2, player);
                    }
                    me->DespawnOrUnsummon(4000);
                    break;
            }
        }

        Creature* GetSpeakerByEntry(int32 entry)
        {
            switch (entry)
            {
                case NPC_RANSHALLA:
                    return me;
                case NPC_VOICE_ELUNE:
                    return me->GetMap()->GetCreature(_voiceEluneGUID);
                case NPC_PRIESTESS_DATA_1:
                    return me->GetMap()->GetCreature(_firstPriestessGUID);
                case NPC_PRIESTESS_DATA_2:
                    return me->GetMap()->GetCreature(_secondPriestessGUID);
                default:
                    return nullptr;
            }

        }

        void UpdateEscortAI(uint32 diff)
        {
            DialogueUpdate(diff);

            if (_delayTimer)
            {
                if (_delayTimer <= diff)
                {
                    SetEscortPaused(false);
                    _delayTimer = 0;
                }
                else
                    _delayTimer -= diff;
            }
            events.Update(diff);
            if (events.ExecuteEvent() == EVENT_RESUME)
                StartNextDialogueText(SAY_PRIESTESS_ALTAR_3);

            npc_escortAI::UpdateEscortAI(diff);
        }
    private:
        EventMap events;
    };
};

/*#####
# go_elune_fire
#####*/

class go_elune_fire : public GameObjectScript
{
public:
    go_elune_fire() : GameObjectScript("go_elune_fire") { }

    bool OnGossipHello(Player* /*player*/, GameObject* go) override
    {
        // Check if we are using the torches or the altar
        bool isAltar = false;

        if (go->GetEntry() == GO_ELUNE_ALTAR)
            isAltar = true;

        if (Creature* ranshalla = GetClosestCreatureWithEntry(go, NPC_RANSHALLA, 10.0f))
        {
            if (npc_ranshalla::npc_ranshallaAI* escortAI = dynamic_cast<npc_ranshalla::npc_ranshallaAI*>(ranshalla->AI()))
                escortAI->DoContinueEscort(isAltar);
        }

        go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE);

        return false;
    }
};

void AddSC_winterspring()
{
    // Ours
    new npc_stave_of_the_ancients();

    // Theirs
    new npc_rivern_frostwind();
    new npc_ranshalla();
    new go_elune_fire();
}
